home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD 2.1 / Amiga Developer CD v2.1.iso / Reference / DevCon / Washington_1988 / Devcon_Extras / Example_Code / SampleDevice / ramdev.device.asm < prev    next >
Encoding:
Assembly Source File  |  1992-08-27  |  44.4 KB  |  1,403 lines

  1. *************************************************************************
  2. *
  3. *   Copyright (C) 1986,1988 Commodore Amiga Inc.  All rights reserved.
  4. *   Permission granted for non-commercial use.
  5. *
  6. *************************************************************************
  7. *
  8. * ramdev.asm -- Skeleton device code.
  9. *
  10. * A sample 4 unit ramdisk that can be bound to an expansion slot device,
  11. * or used without.  Works with the Fast File System.
  12. * This code is required reading for device driver writers.  It contains
  13. * information not found elsewhere.
  14. *
  15. * This example includes a task, though a task is not actually needed for
  16. * a simple ram disk.  Unlike a single set of hardware registers that
  17. * may need to be shared by multiple tasks, ram can be freely shared.
  18. * This example does not show arbitration of hardware resources.
  19. *
  20. * Tested with CAPE and Metacomco
  21. *
  22. *       Based on mydev.asm
  23. *       10/07/86 Modified by Lee Erickson to be a simple disk device
  24. *            using RAM to simulate a disk.
  25. *       02/02/88 Modified by C. Scheppner, renamed ramdev
  26. *       09/28/88 Repaired by Bryce Nesbitt for new release
  27. *       11/02/88 More clarifications
  28. *       02/01/89 Even more clarifications & warnings
  29. *       02/22/89 START/STOP fix from Marco Papa
  30. *************************************************************************
  31.  
  32.    SECTION firstsection
  33.  
  34.    NOLIST
  35.    include "exec/types.i"
  36.    include "exec/devices.i"
  37.    include "exec/initializers.i"
  38.    include "exec/memory.i"
  39.    include "exec/resident.i"
  40.    include "exec/io.i"
  41.    include "exec/ables.i"
  42.    include "exec/errors.i"
  43.    include "exec/tasks.i"
  44.    include "hardware/intbits.i"
  45.    IFNE AUTOMOUNT
  46.    include "libraries/expansion.i"
  47.    include "libraries/configvars.i"
  48.    include "libraries/configregs.i"
  49.    ENDC
  50.  
  51.    include "asmsupp.i"  ;standard asmsupp.i, same as used for library
  52.    LIST
  53.    include "ramdev.i"
  54.  
  55.  
  56. ABSEXECBASE equ 4   ;Absolute location of the pointer to exec.library base
  57.  
  58.  
  59.    ;------ These don't have to be external, but it helps some
  60.    ;------ debuggers to have them globally visible
  61.    XDEF   Init
  62.    XDEF   Open
  63.    XDEF   Close
  64.    XDEF   Expunge
  65.    XDEF   Null
  66.    XDEF   myName
  67.    XDEF   BeginIO
  68.    XDEF   AbortIO
  69.  
  70.    ;Pull these _LVOs in from amiga.lib
  71.    XLIB   AddIntServer
  72.    XLIB   RemIntServer
  73.    XLIB   Debug
  74.    XLIB   InitStruct
  75.    XLIB   OpenLibrary
  76.    XLIB   CloseLibrary
  77.    XLIB   Alert
  78.    XLIB   FreeMem
  79.    XLIB   Remove
  80.    XLIB   AddPort
  81.    XLIB   AllocMem
  82.    XLIB   AddTask
  83.    XLIB   PutMsg
  84.    XLIB   RemTask
  85.    XLIB   ReplyMsg
  86.    XLIB   Signal
  87.    XLIB   GetMsg
  88.    XLIB   Wait
  89.    XLIB   WaitPort
  90.    XLIB   AllocSignal
  91.    XLIB   SetTaskPri
  92.    XLIB   GetCurrentBinding    ;Use to get list of boards for this driver
  93.    XLIB   MakeDosNode
  94.    XLIB   AddDosNode
  95.    XLIB   CopyMemQuick    ;Highly optimized copy function from exec.library
  96.  
  97.    INT_ABLES        ;Macro from exec/ables.i
  98.  
  99.  
  100. ;-----------------------------------------------------------------------
  101. ; The first executable location.  This should return an error
  102. ; in case someone tried to run you as a program (instead of
  103. ; loading you as a device).
  104.  
  105. FirstAddress:
  106.         moveq    #-1,d0
  107.         rts
  108.  
  109. ;-----------------------------------------------------------------------
  110. ; A romtag structure.  After your driver is brought in from disk, the
  111. ; disk image will be scanned for this structure to discover magic constants
  112. ; about you (such as where to start running you from...).
  113. ;-----------------------------------------------------------------------
  114.  
  115.    ; Most people will not need a priority and should leave it at zero.
  116.    ; the RT_PRI field is used for configuring the roms.  Use "mods" from
  117.    ; wack to look at the other romtags in the system
  118. MYPRI    EQU   0
  119.  
  120. initDDescrip:
  121.                 ;STRUCTURE RT,0
  122.      DC.W    RTC_MATCHWORD    ; UWORD RT_MATCHWORD (Magic cookie)
  123.      DC.L    initDDescrip    ; APTR    RT_MATCHTAG  (Back pointer)
  124.      DC.L    EndCode        ; APTR    RT_ENDSKIP   (To end of this hunk)
  125.      DC.B    RTF_AUTOINIT    ; UBYTE RT_FLAGS     (magic-see "Init:")
  126.      DC.B    VERSION        ; UBYTE RT_VERSION
  127.      DC.B    NT_DEVICE        ; UBYTE RT_TYPE      (must be correct)
  128.      DC.B    MYPRI        ; BYTE    RT_PRI
  129.      DC.L    myName        ; APTR    RT_NAME      (exec name)
  130.      DC.L    idString        ; APTR    RT_IDSTRING  (text string)
  131.      DC.L    Init        ; APTR    RT_INIT
  132.            ; LABEL RT_SIZE
  133.  
  134.  
  135.    ;This name for debugging use
  136.    IFNE INFO_LEVEL  ;If any debugging enabled at all
  137. subSysName:
  138.     dc.b    "ramdev",0
  139.    ENDC
  140.  
  141.    ; this is the name that the device will have
  142. myName:      MYDEVNAME
  143.  
  144.  IFNE  AUTOMOUNT
  145. ExLibName    dc.b 'expansion.library',0   ; Expansion Library Name
  146.  ENDC
  147.  
  148.    ; a major version number.
  149. VERSION:    EQU   1
  150.  
  151.    ; A particular revision.  This should uniquely identify the bits in the
  152.    ; device.  I use a script that advances the revision number each time
  153.    ; I recompile.  That way there is never a question of which device
  154.    ; that really is.
  155. REVISION:   EQU   35
  156.  
  157.    ; this is an identifier tag to help in supporting the device
  158.    ; format is 'name version.revision (dd MON yyyy)',<cr>,<lf>,<null>
  159. idString:   dc.b   'ramdev 1.35 (22 Feb 1989)',13,10,0
  160.  
  161.    ; force word alignment
  162.    ds.w   0
  163.  
  164.  
  165.    ; The romtag specified that we were "RTF_AUTOINIT".  This means
  166.    ; that the RT_INIT structure member points to one of these
  167.    ; tables below.  If the AUTOINIT bit was not set then RT_INIT
  168.    ; would point to a routine to run.
  169.  
  170. Init:
  171.    DC.L   MyDev_Sizeof        ; data space size
  172.    DC.L   funcTable        ; pointer to function initializers
  173.    DC.L   dataTable        ; pointer to data initializers
  174.    DC.L   initRoutine        ; routine to run
  175.  
  176.  
  177. funcTable:
  178.    ;------ standard system routines
  179.    dc.l   Open
  180.    dc.l   Close
  181.    dc.l   Expunge
  182.    dc.l   Null        ;Reserved for future use!
  183.  
  184.    ;------ my device definitions
  185.    dc.l   BeginIO
  186.    dc.l   AbortIO
  187.  
  188.    ;------ custom extended functions
  189.    dc.l   FunctionA
  190.    dc.l   FunctionB
  191.  
  192.    ;------ function table end marker
  193.    dc.l   -1
  194.  
  195.  
  196.    ;The data table initializes static data structures. The format is
  197.    ;specified in exec/InitStruct routine's manual pages.  The
  198.    ;INITBYTE/INITWORD/INITLONG macros are in the file "exec/initializers.i".
  199.    ;The first argument is the offset from the device base for this
  200.    ;byte/word/long. The second argument is the value to put in that cell.
  201.    ;The table is null terminated
  202.    ;
  203. dataTable:
  204.    INITBYTE   LN_TYPE,NT_DEVICE       ;Must be LN_TYPE!
  205.    INITLONG   LN_NAME,myName
  206.    INITBYTE   LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
  207.    INITWORD   LIB_VERSION,VERSION
  208.    INITWORD   LIB_REVISION,REVISION
  209.    INITLONG   LIB_IDSTRING,idString
  210.    DC.L   0
  211.  
  212.  
  213. ;-------- initRoutine -------------------------------------------------------
  214. ;
  215. ; FOR RTF_AUTOINIT:
  216. ;   This routine gets called after the device has been allocated.
  217. ;   The device pointer is in D0.  The AmigaDOS segment list is in a0.
  218. ;   If it returns the device pointer, then the device will be linked
  219. ;   into the device list.  If it returns NULL, then the device
  220. ;   will be unloaded.
  221. ;
  222. ; IMPORTANT:
  223. ;   If you don't use the "RTF_AUTOINIT" feature, there is an additional
  224. ;   caveat.  If you allocate memory in your Open function, remember that
  225. ;   allocating memory can cause an Expunge... including an expunge of your
  226. ;   device.  This must not be fatal.  The easy solution is don't add your
  227. ;   device to the list until after it is ready for action.
  228. ;
  229. ; This call is single-threaded by exec; please read the description for
  230. ; "Open" below.
  231. ;
  232. ; Register Usage
  233. ; ==============
  234. ; a3 -- Points to temporary RAM
  235. ; a4 -- Expansion library base
  236. ; a5 -- device pointer
  237. ; a6 -- Exec base
  238. ;----------------------------------------------------------------------
  239. initRoutine:
  240.    ;------ get the device pointer into a convenient A register
  241.    PUTMSG   5,<'%s/Init: called'>
  242.    movem.l  d1-d7/a0-a5,-(sp)   ; Preserve ALL modified registers
  243.    move.l   d0,a5
  244.  
  245.    ;------ save a pointer to exec
  246.    move.l   a6,md_SysLib(a5)    ;faster access than move.l 4,a6
  247.  
  248.    ;------ save pointer to our loaded code (the SegList)
  249.    move.l   a0,md_SegList(a5)
  250.  
  251.  IFNE  AUTOMOUNT
  252. **************************************************************************
  253. *
  254. * Here starts the AutoConfig stuff.  If this driver was to be tied to
  255. * an expansion board, you would put this driver in the expansion drawer,
  256. * and be called when BindDrivers finds a board that matches this driver.
  257. * The Commodore-Amiga assigned product number of your board must be
  258. * specified in the "PRODUCT=" field in the TOOLTYPES of this driver's icon.
  259. * GetCurrentBinding() returns your (first) board.
  260. *
  261.    lea.l     ExLibName,A1    ; Get expansion lib. name
  262.    moveq.l   #0,D0
  263.    CALLSYS   OpenLibrary    ; Open the expansion library
  264.    tst.l     D0
  265.    beq         Init_Error
  266.  
  267.    ;------ init_OpSuccess:
  268.    move.l    D0,A4        ;[expansionbase to A4]
  269.    moveq     #0,D3
  270.    lea         md_Base(A5),A0   ; Get the Current Bindings
  271.    moveq     #4,D0          ; Just get address (length = 4 bytes)
  272.    LINKLIB   _LVOGetCurrentBinding,A4
  273.    move.l    md_Base(A5),D0      ; Get start of list
  274.    tst.l     D0        ; If controller not found
  275.    beq         Init_End       ; Exit and unload driver
  276.  
  277.    PUTMSG    10,<'%s/Init: GetCurrentBinding returned non-zero'>
  278.    move.l    D0,A0       ; Get config structure address
  279.    move.l    cd_BoardAddr(A0),md_Base(A5); Save board base address
  280.    bclr.b    #CDB_CONFIGME,cd_Flags(A0); Mark board as configured
  281.  
  282. ;----------------------------------------------------------------------
  283. ;
  284. ; Here we build a packet describing the characteristics of our disk to
  285. ; pass to AmigaDOS.  This serves the same purpose as a "mount" command
  286. ; of this device would.  For disks, it might be useful to actually
  287. ; get this information right from the disk itself.  Just as mount,
  288. ; it could be for multiple partitions on the single physical device.
  289. ; For this example, we will simply hard code the appropriate parameters.
  290. ;
  291. ; The AddDosNode call adds things to dos's list without needing to
  292. ; use mount.  We'll mount all 4 of our units whenever we are
  293. ; started.
  294. ;
  295. ;-----------------------------------------------------------------------
  296.  
  297. ;!!! If your card was successfully configured, you can mount the
  298. ;!!! units as DOS nodes
  299.  
  300.    ;------   Allocate temporary RAM to build MakeDosNode parameter packet
  301.    move.l    #MEMF_CLEAR!MEMF_PUBLIC,d1
  302.    move.l    #mdn_Sizeof,d0   ; Enough room for our parameter packet
  303.    CALLSYS   AllocMem
  304.    move.l    d0,a3
  305.  
  306.    ;-----   Use InitStruct to initialize the constant portion of packet
  307.    move.l    d0,a2       ; Point to memory to initialize
  308.    moveq.l   #0,d0       ; Don't need to re-zero it
  309.    lea.l     mdn_Init(pc),A1
  310.    CALLSYS   InitStruct
  311.  
  312.    lea         mdn_dName(a3),a0     ; Get addr of Device name
  313.    move.l    a0,mdn_dosName(a3)   ;   and save in environment
  314.  
  315.    moveq     #0,d6          ; Now tell AmigaDOS about all units UNITNUM
  316. Uloop:
  317.    move.b    d6,d0         ; Get unit number
  318.    add.b     #$30,d0         ; Make ASCII, minus 1
  319.    move.b    d0,mdn_dName+2(a3)   ;   and store in name
  320.    move.l    d6,mdn_unit(a3)      ; Store unit # in environment
  321.  
  322. ;
  323. ;! Before adding to the dos list, you should really check if you
  324. ;! are about to cause a name collision.  This example does not.
  325. ;
  326.  
  327.    move.l    a3,a0
  328.    LINKLIB   _LVOMakeDosNode,a4   ; Build AmigaDOS structures
  329.    ;This can fail, but so what?
  330.    move.l    d0,a0          ; Get deviceNode address
  331.    moveq.l   #0,d0          ; Set device priority to 0
  332.    moveq.l   #0,d1
  333. *  moveq.l   #ADNF_STARTPROC,d1     ; See note below
  334.    ;It's ok to pass a zero in here
  335.    LINKLIB   _LVOAddDosNode,a4
  336.  
  337.  
  338. ; ADNF_STARTPROC will work, but only if dn_SegList is filled in
  339. ; in the SegPtr of the handler task.
  340.  
  341.  
  342.    addq     #1,d6      ; Bump unit number
  343.    cmp.b    #MD_NUMUNITS,d6
  344.    bls.s    Uloop      ; Loop until all units installed
  345.  
  346.    move.l   a3,a1      ; Return RAM to system
  347.    move.l   #mdn_Sizeof,d0
  348.    CALLSYS  FreeMem
  349.  
  350. Init_End:
  351.  
  352.    move.l   a4,a1      ; Now close expansion library
  353.    CALLSYS  CloseLibrary
  354. *
  355. *   You would normally set d0 to a NULL if your initialization failed,
  356. *   but I'm not doing that for this demo, since it is unlikely
  357. *   you actually have a board with any particular manufacturer ID
  358. *   installed when running this demo.
  359. *************************************************************************
  360.   ENDC
  361.  
  362.    move.l   a5,d0
  363. Init_Error:
  364.    movem.l  (sp)+,d1-d7/a0-a5
  365.    rts
  366.  
  367.  
  368. ;----------------------------------------------------------------------
  369. ;
  370. ; Here begins the system interface commands.  When the user calls
  371. ; OpenDevice/CloseDevice/RemDevice, this eventually gets translated
  372. ; into a call to the following routines (Open/Close/Expunge).
  373. ; Exec has already put our device pointer in a6 for us.
  374. ;
  375. ; IMPORTANT:
  376. ;   These calls are guaranteed to be single-threaded; only one task
  377. ;   will execute your Open/Close/Expunge at a time.
  378. ;
  379. ;   For Kickstart V33/34, the single-threading method involves "Forbid".
  380. ;   There is a good chance this will change.  Anything inside your
  381. ;   Open/Close/Expunge that causes a direct or indirect Wait() will break
  382. ;   the Forbid().  If the Forbid() is broken, some other task might
  383. ;   manage to enter your Open/Close/Expunge code at the same time.
  384. ;   Take care!
  385. ;
  386. ; Since exec has turned off task switching while in these routines
  387. ; (via Forbid/Permit), we should not take too long in them.
  388. ;
  389. ;----------------------------------------------------------------------
  390.  
  391.    ; Open sets the IO_ERROR field on an error.    If it was successfull,
  392.    ; we should also set up the IO_UNIT and LN_TYPE fields.
  393.    ; exec takes care of setting up IO_DEVICE.
  394.  
  395. Open:       ; ( device:a6, iob:a1, unitnum:d0, flags:d1 )
  396.  
  397. ;** Subtle point: any AllocMem() call can cause a call to this device's
  398. ;** expunge vector.  If LIB_OPENCNT is zero, the device might get expunged.
  399.    addq.w   #1,LIB_OPENCNT(a6)  ;Fake an opener for duration of call <|>
  400.  
  401.    PUTMSG   20,<'%s/Open: called'>
  402.    movem.l  d2/a2/a3/a4,-(sp)
  403.  
  404.    move.l   a1,a2      ; save the iob
  405.  
  406.    ;------ see if the unit number is in range    *!* UNIT 0 to 3 *!*
  407.    cmp.l   #MD_NUMUNITS,d0
  408.    bcc.s   Open_Range_Error   ; unit number out of range (BHS)
  409.  
  410.    ;------ see if the unit is already initialized
  411.    move.l   d0,d2      ; save unit number
  412.    lsl.l    #2,d0
  413.    lea.l    md_Units(a6,d0.l),a4
  414.    move.l   (a4),d0
  415.    bne.s    Open_UnitOK
  416.  
  417.    ;------ try and conjure up a unit
  418.    bsr        InitUnit    ;scratch:a3 unitnum:d2 devpoint:a6
  419.  
  420.    ;------ see if it initialized OK
  421.    move.l   (a4),d0
  422.    beq.s    Open_Error
  423.  
  424. Open_UnitOK:
  425.    move.l   d0,a3      ; unit pointer in a3
  426.    move.l   d0,IO_UNIT(a2)
  427.  
  428.    ;------ mark us as having another opener
  429.    addq.w   #1,LIB_OPENCNT(a6)
  430.    addq.w   #1,UNIT_OPENCNT(a3)     ;Internal bookkeeping
  431.  
  432.    ;------ prevent delayed expunges
  433.    bclr     #LIBB_DELEXP,md_Flags(a6)
  434.  
  435.    CLEAR    d0
  436.    move.b   d0,IO_ERROR(a2)
  437.    move.b   #NT_REPLYMSG,LN_TYPE(a2) ;IMPORTANT: Mark IORequest as "complete"
  438.  
  439. Open_End:
  440.  
  441.    subq.w   #1,LIB_OPENCNT(a6) ;** End of expunge protection <|>
  442.    movem.l  (sp)+,d2/a2/a3/a4
  443.    rts
  444.  
  445. Open_Range_Error:
  446. Open_Error:
  447.    moveq    #IOERR_OPENFAIL,d0
  448.    move.b   d0,IO_ERROR(a2)
  449.    move.l   d0,IO_DEVICE(a2)    ;IMPORTANT: trash IO_DEVICE on open failure
  450.    PUTMSG   2,<'%s/Open: failed'>
  451.    bra.s    Open_End
  452.  
  453.  
  454. ;----------------------------------------------------------------------------
  455. ; There are two different things that might be returned from the Close
  456. ; routine.  If the device wishes to be unloaded, then Close must return
  457. ; the segment list (as given to Init).  Otherwise close MUST return NULL.
  458.  
  459. Close:        ; ( device:a6, iob:a1 )
  460.    movem.l  d1/a2-a3,-(sp)
  461.    PUTMSG   20,<'%s/Close: called'>
  462.  
  463.    move.l   a1,a2
  464.  
  465.    move.l   IO_UNIT(a2),a3
  466.  
  467.    ;------ IMPORTANT: make sure the IORequest is not used again
  468.    ;------ with a -1 in IO_DEVICE, any BeginIO() attempt will
  469.    ;------ immediatly halt (which is better than a subtle corruption
  470.    ;------ that will lead to hard-to-trace crashes!!!!!!!!!!!!!!!!!!
  471.    moveq.l  #-1,d0
  472.    move.l   d0,IO_UNIT(a2)      ;We're closed...
  473.    move.l   d0,IO_DEVICE(a2)    ;customers not welcome at this IORequest!!
  474.  
  475.    ;------ see if the unit is still in use
  476.    subq.w   #1,UNIT_OPENCNT(a3)
  477.  
  478. ;!!!!!! Since this example is a RAM disk (and we don't want the contents to
  479. ;!!!!!! disappear between opens, ExpungeUnit will be skipped here.  It would
  480. ;!!!!!! be used for drivers of "real" devices
  481. ;!!!!!!   bne.s   Close_Device
  482. ;!!!!!!   bsr      ExpungeUnit
  483.  
  484. Close_Device:
  485.    CLEAR   d0
  486.    ;------ mark us as having one fewer openers
  487.    subq.w  #1,LIB_OPENCNT(a6)
  488.  
  489.    ;------ see if there is anyone left with us open
  490.    bne.s   Close_End
  491.  
  492.    ;------ see if we have a delayed expunge pending
  493.    btst    #LIBB_DELEXP,md_Flags(a6)
  494.    beq.s   Close_End
  495.  
  496.    ;------ do the expunge
  497.    bsr       Expunge
  498.  
  499. Close_End:
  500.    movem.l   (sp)+,d1/a2-a3
  501.    rts                ;MUST return either zero or the SegList!!!
  502.  
  503.  
  504. ;------- Expunge -----------------------------------------------------------
  505. ;
  506. ; Expunge is called by the memory allocator when the system is low on
  507. ; memory.
  508. ;
  509. ; There are two different things that might be returned from the Expunge
  510. ; routine.  If the device is no longer open then Expunge may return the
  511. ; segment list (as given to Init).  Otherwise Expunge may set the
  512. ; delayed expunge flag and return NULL.
  513. ;
  514. ; One other important note: because Expunge is called from the memory
  515. ; allocator, it may NEVER Wait() or otherwise take long time to complete.
  516. ;
  517. ;    A6        - library base (scratch)
  518. ;    D0-D1/A0-A1 - scratch
  519. ;
  520. Expunge:   ; ( device: a6 )
  521.    PUTMSG   10,<'%s/Expunge: called'>
  522.  
  523.    movem.l  d1/d2/a5/a6,-(sp)   ; Save ALL modified registers
  524.    move.l   a6,a5
  525.    move.l   md_SysLib(a5),a6
  526.  
  527.    ;------ see if anyone has us open
  528.    tst.w   LIB_OPENCNT(a5)
  529. ;!!!!!    The following line is commented out for this RAM disk demo, since
  530. ;!!!!!    we don't want the RAM to be freed after FORMAT, for example.
  531. ;   beq    1$
  532.  
  533.    ;------ it is still open.  set the delayed expunge flag
  534.    bset    #LIBB_DELEXP,md_Flags(a5)
  535.    CLEAR   d0
  536.    bra.s   Expunge_End
  537.  
  538. 1$:
  539.    ;------ go ahead and get rid of us.    Store our seglist in d2
  540.    move.l   md_SegList(a5),d2
  541.  
  542.    ;------ unlink from device list
  543.    move.l    a5,a1
  544.    CALLSYS   Remove        ;Remove first (before FreeMem)
  545.  
  546.    ;
  547.    ; device specific closings here...
  548.    ;
  549.  
  550.    ;------ free our memory (must calculate from LIB_POSSIZE & LIB_NEGSIZE)
  551.    move.l   a5,a1        ;Devicebase
  552.    CLEAR    d0
  553.    move.w   LIB_NEGSIZE(a5),d0
  554.    suba.l   d0,a1        ;Calculate base of functions
  555.    add.w    LIB_POSSIZE(a5),d0  ;Calculate size of functions + data area
  556.    CALLSYS  FreeMem
  557.  
  558.    ;------ set up our return value
  559.    move.l   d2,d0
  560.  
  561. Expunge_End:
  562.    movem.l  (sp)+,d1/d2/a5/a6
  563.    rts
  564.  
  565.  
  566. ;------- Null ---------------------------------------------------------------
  567. Null:
  568.    PUTMSG  1,<'%s/Null: called'>
  569.    CLEAR   d0
  570.    rts        ;The "Null" function MUST return NULL.
  571.  
  572.  
  573. ;------- Custom ------------------------------------------------------------
  574. ;
  575. ;Two "do nothing" device-specific functions
  576. ;
  577. FunctionA:
  578.     add.l   d1,d0   ;Add
  579.     rts
  580. FunctionB:
  581.     add.l   d0,d0   ;Double
  582.     rts
  583.  
  584.  
  585. ****************************************************************************
  586.  
  587. InitUnit:   ; ( d2:unit number, a3:scratch, a6:devptr )
  588.    PUTMSG   30,<'%s/InitUnit: called'>
  589.    movem.l  d2-d4/a2,-(sp)
  590.  
  591.    ;------ allocate unit memory
  592.    move.l   #MyDevUnit_Sizeof,d0
  593.    move.l   #MEMF_PUBLIC!MEMF_CLEAR,d1
  594.    LINKSYS  AllocMem,md_SysLib(a6)
  595.    tst.l    d0
  596.    beq        InitUnit_End
  597.    move.l   d0,a3
  598.  
  599.    moveq.l  #0,d0       ; Don't need to re-zero it
  600.    move.l   a3,a2       ; InitStruct is initializing the UNIT
  601.    lea.l    mdu_Init(pc),A1
  602.    LINKSYS  InitStruct,md_SysLib(a6)
  603.  
  604.    ;!! IMPORTANT !!
  605.    move.l   #42414400,mdu_RAM(a3)   ;Mark offset zero as ASCII "BAD "
  606.    ;!! IMPORTANT !!
  607.  
  608.    move.b   d2,mdu_UnitNum(a3)      ;initialize unit number
  609.    move.l   a6,mdu_Device(a3)       ;initialize device pointer
  610.  
  611.    ;------ start up the unit task.  We do a trick here --
  612.    ;------ we set his message port to PA_IGNORE until the
  613.    ;------ new task has a change to set it up.
  614.    ;------ We cannot go to sleep here: it would be very nasty
  615.    ;------ if someone else tried to open the unit
  616.    ;------ (exec's OpenDevice has done a Forbid() for us --
  617.    ;------ we depend on this to become single threaded).
  618.  
  619.    ;------ Initialize the stack information
  620.    lea        mdu_stack(a3),a0          ; Low end of stack
  621.    move.l   a0,mdu_tcb+TC_SPLOWER(a3)
  622.    lea        MYPROCSTACKSIZE(a0),a0    ; High end of stack
  623.    move.l   a0,mdu_tcb+TC_SPUPPER(a3)
  624.    move.l   a3,-(A0)                  ; argument -- unit ptr (send on stack)
  625.    move.l   a0,mdu_tcb+TC_SPREG(a3)
  626.    lea        mdu_tcb(a3),a0
  627.    move.l   a0,MP_SIGTASK(a3)
  628.  
  629.    IFGE INFO_LEVEL-30
  630.        move.l    a0,-(SP)
  631.        move.l    a3,-(SP)
  632.        PUTMSG    30,<'%s/InitUnit, unit= %lx, task=%lx'>
  633.        addq.l    #8,sp
  634.    ENDC
  635.  
  636.    ;------ initialize the unit's message port's list
  637.    lea        MP_MSGLIST(a3),a0
  638.    NEWLIST  a0            ;<- IMPORTANT! Lists MUST! have NEWLIST
  639.                 ;work magic on them before use.  (AddPort()
  640.                 ;can do this for you)
  641.  
  642.    IFD     INTRRUPT
  643.    move.l   a3,mdu_is+IS_DATA(a3)   ; Pass unit addr to interrupt server
  644.    ENDC
  645.  
  646. ;   Startup the task
  647.    lea        mdu_tcb(a3),a1
  648.    lea        Task_Begin(PC),a2
  649.    move.l   a3,-(sp)      ; Preserve UNIT pointer
  650.    lea        -1,a3      ; generate address error
  651.               ; if task ever "returns" (we RemTask() it
  652.               ; to get rid of it...)
  653.    CLEAR   d0
  654.    PUTMSG   30,<'%s/About to add task'>
  655.    LINKSYS AddTask,md_SysLib(a6)
  656.    move.l   (sp)+,a3      ; restore UNIT pointer
  657.  
  658.    ;------ mark us as ready to go
  659.    move.l   d2,d0      ; unit number
  660.    lsl.l    #2,d0
  661.    move.l   a3,md_Units(a6,d0.l)   ; set unit table
  662.    PUTMSG   30,<'%s/InitUnit: ok'>
  663.  
  664. InitUnit_End:
  665.    movem.l   (sp)+,d2-d4/a2
  666.    rts
  667.  
  668.  
  669. ;---------------------------------------------------------------------------
  670. FreeUnit:   ; ( a3:unitptr, a6:deviceptr )
  671.    move.l   a3,a1
  672.    move.l   #MyDevUnit_Sizeof,d0
  673.    LINKSYS  FreeMem,md_SysLib(a6)
  674.    rts
  675.  
  676. ;---------------------------------------------------------------------------
  677. ExpungeUnit:   ; ( a3:unitptr, a6:deviceptr )
  678.    PUTMSG   10,<'%s/ExpungeUnit: called'>
  679.    move.l   d2,-(sp)
  680.  
  681. ;
  682. ; If you can expunge you unit, and each unit has it's own interrupts,
  683. ; you must remember to remove its interrupt server
  684. ;
  685.  
  686.    IFD     INTRRUPT
  687.    lea.l   mdu_is(a3),a1              ; Point to interrupt structure
  688.    moveq   #INTB_PORTS,d0          ; Portia interrupt bit 3
  689.    LINKSYS RemIntServer,md_SysLib(a6) ;Now remove the interrupt server
  690.    ENDC
  691.  
  692.    ;------ get rid of the unit's task.  We know this is safe
  693.    ;------ because the unit has an open count of zero, so it
  694.    ;------ is 'guaranteed' not in use.
  695.    lea     mdu_tcb(a3),a1
  696.    LINKSYS RemTask,md_SysLib(a6)
  697.  
  698.    ;------ save the unit number
  699.    CLEAR   d2
  700.    move.b  mdu_UnitNum(a3),d2
  701.  
  702.    ;------ free the unit structure.
  703.    bsr       FreeUnit
  704.  
  705.    ;------ clear out the unit vector in the device
  706.    lsl.l   #2,d2
  707.    clr.l   md_Units(a6,d2.l)
  708.  
  709.    move.l  (sp)+,d2
  710.    rts
  711.  
  712.  
  713.  
  714. *****************************************************************************
  715. ;
  716. ; here begins the device functions
  717. ;
  718. ;----------------------------------------------------------------------------
  719. ; cmdtable is used to look up the address of a routine that will
  720. ; implement the device command.
  721. ;
  722. ; NOTE: the "extended" commands (ETD_READ/ETD_WRITE) have bit 15 set!
  723. ; We deliberately refuse to operate on such commands.  However a driver
  724. ; that supports removable media may want to implement this.  One
  725. ; open issue is the handling of the "seclabel" area. It is probably
  726. ; best to reject any command with a non-null "seclabel" pointer.
  727. ;
  728. cmdtable:
  729.    DC.L   Invalid    ;$00000001  ;0    CMD_INVALID
  730.    DC.L   MyReset    ;$00000002  ;1    CMD_RESET
  731.    DC.L   RdWrt     ;$00000004  ;2    CMD_READ    (\/common)
  732.    DC.L   RdWrt     ;$00000008  ;3    CMD_WRITE    (/\common)  ETD_
  733.    DC.L   Update    ;$00000010  ;4    CMD_UPDATE    (NO-OP)     ETD_
  734.    DC.L   Clear     ;$00000020  ;5    CMD_CLEAR    (NO-OP)     ETD_
  735.    DC.L   MyStop    ;$00000040  ;6    CMD_STOP            ETD_
  736.    DC.L   Start     ;$00000080  ;7    CMD_START
  737.    DC.L   Flush     ;$00000100  ;8    CMD_FLUSH
  738.    DC.L   Motor     ;$00000200  ;9    TD_MOTOR    (NO-OP)     ETD_
  739.    DC.L   Seek        ;$00000400  ;A    TD_SEEK     (NO-OP)     ETD_
  740.    DC.L   RdWrt     ;$00000800  ;B    TD_FORMAT    (Same as write)
  741.    DC.L   MyRemove    ;$00001000  ;C    TD_REMOVE    (NO-OP)
  742.    DC.L   ChangeNum    ;$00002000  ;D    TD_CHANGENUM    (returns 0)
  743.    DC.L   ChangeState    ;$00004000  ;E    TD_CHANGESTATE    (returns 0)
  744.    DC.L   ProtStatus    ;$00008000  ;F    TD_PROTSTATUS    (returns 0)
  745.    DC.L   RawRead    ;$00010000  ;10 TD_RAWREAD    (INVALID)
  746.    DC.L   RawWrite    ;$00020000  ;11 TD_RAWWRITE    (INVALID)
  747.    DC.L   GetDriveType    ;$00040000  ;12 TD_GETDRIVETYPE (Returns 1)
  748.    DC.L   GetNumTracks    ;$00080000  ;13 TD_GETNUMTRACKS (Returns NUMTRKS)
  749.    DC.L   AddChangeInt    ;$00100000  ;14 TD_ADDCHANGEINT (NO-OP)
  750.    DC.L   RemChangeInt    ;$00200000  ;15 TD_REMCHANGEINT (NO-OP)
  751. cmdtable_end:
  752.  
  753. ; this define is used to tell which commands should be handled
  754. ; immediately (on the caller's schedule).
  755. ;
  756. ; The immediate commands are Invalid, Reset, Stop, Start, Flush
  757. ;
  758. ; Note that this method limits you to just 32 device specific commands,
  759. ; which may not be enough.
  760. ;IMMEDIATES   EQU   %00000000000000000000000111000011
  761. ;;             --------========--------========
  762. ;;             FEDCBA9876543210FEDCBA9876543210
  763.  
  764. ;;An alternate version.  All commands that are trivially short
  765. ;;and %100 reentrant are included.  This way you won't get the
  766. ;;task switch overhead for these commands.
  767. ;;
  768. IMMEDIATES   EQU   %11111111111111111111011111110011
  769. ;            --------========--------========
  770. ;            FEDCBA9876543210FEDCBA9876543210
  771.  
  772.     IFD   INTRRUPT   ; if using interrupts,
  773. ; These commands can NEVER be done "immediately" if using interrupts,
  774. ; since they would "wait" for the interrupt forever!
  775. ; Read, Write, Format
  776. NEVERIMMED   EQU   $0000080C
  777.     ENDC
  778.  
  779.  
  780. ;--------------------------------
  781. ; BeginIO starts all incoming io.  The IO is either queued up for the
  782. ; unit task or processed immediately.
  783. ;
  784. ;
  785. ; BeginIO often is given the responsibility of making devices single
  786. ; threaded... so two tasks sending commands at the same time don't cause
  787. ; a problem.  Once this has been done, the command is dispatched via
  788. ; PerformIO.
  789. ;
  790. ; There are many ways to do the threading.  This example uses the
  791. ; UNITB_ACTIVE bit.  Be sure this is good enough for your device before
  792. ; using!  Any method is ok.  If immediate access can not be obtained, the
  793. ; request is queued for later processing.
  794. ;
  795. ; Some IO requests do not need single threading, these can be performed
  796. ; immediatley.
  797. ;
  798. ; IMPORTANT:
  799. ;   The exec WaitIO() function uses the IORequest node type (LN_TYPE)
  800. ;   as a flag.    If set to NT_MESSAGE, it assumes the request is
  801. ;   still pending and will wait.  If set to NT_REPLYMSG, it assumes the
  802. ;   request is finished.  It's the responsibility of the device driver
  803. ;   to set the node type to NT_MESSAGE before returning to the user.
  804. ;
  805. BeginIO:   ; ( iob: a1, device:a6 )
  806.  
  807.     IFGE INFO_LEVEL-1
  808.     bchg.b    #1,$bfe001  ;Blink the power LED
  809.     ENDC
  810.     IFGE INFO_LEVEL-3
  811.      clr.l    -(sp)
  812.      move.w   IO_COMMAND(a1),2(sp)  ;Get entire word
  813.      PUTMSG   3,<'%s/BeginIO  --  %ld'>
  814.      addq.l   #4,sp
  815.     ENDC
  816.  
  817.     movem.l   d1/a0/a3,-(sp)
  818.  
  819.     move.b  #NT_MESSAGE,LN_TYPE(a1) ;So WaitIO() is guaranteed to work
  820.     move.l  IO_UNIT(a1),a3          ;bookkeeping -> what unit to play with
  821.     move.w  IO_COMMAND(a1),d0
  822.  
  823.     ;Do a range check & make sure ETD_XXX type requests are rejected
  824.     cmp.w   #MYDEV_END,d0    ;Compare all 16 bits
  825.     bcc     BeginIO_NoCmd    ;no, reject it.  (bcc=bhs - unsigned)
  826.  
  827.     ;------ process all immediate commands no matter what
  828.     move.l  #IMMEDIATES,d1
  829.     DISABLE a0            ;<-- Ick, nasty stuff, but needed here.
  830.     btst.l  d0,d1
  831.     bne     BeginIO_Immediate
  832.  
  833.     IFD   INTRRUPT   ; if using interrupts,
  834.      ;------ queue all NEVERIMMED commands no matter what
  835.      move.w  #NEVERIMMED,d1
  836.      btst    d0,d1
  837.      bne.s   BeginIO_QueueMsg
  838.     ENDC
  839.  
  840.  
  841.     ;------ see if the unit is STOPPED.  If so, queue the msg.
  842.     btst    #MDUB_STOPPED,UNIT_FLAGS(a3)
  843.     bne     BeginIO_QueueMsg
  844.  
  845.  
  846.     ;------ This is not an immediate command.  See if the device is
  847.     ;------ busy.  If the device is not, do the command on the
  848.     ;------ user schedule.  Else fire up the task.
  849.     ;------ This type of arbitration is not really needed for a ram
  850.     ;------ disk, but is essential for a device to reliably work
  851.     ;------ with shared hardware
  852.     ;------
  853.     ;------ When the lines below are ";" commented out, the task gets
  854.     ;------ a better workout.  When the lines are active, the calling
  855.     ;------ process is usually used for the operation.
  856.     ;------
  857.     ;------ REMEMBER:::: Never Wait() on the user's schedule in BeginIO()!
  858.     ;------ The only exception is when the user has indicated it is ok
  859.     ;------ by setting the "quick" bit.  Since this device copies from
  860.     ;------ ram that never needs to be waited for, this subtlely may not
  861.     ;------ be clear.
  862.     ;------
  863.     bset    #UNITB_ACTIVE,UNIT_FLAGS(a3)   ;<---- comment out these
  864.     beq.s   BeginIO_Immediate           ;<---- lines to test task.
  865.  
  866.  
  867.     ;------ we need to queue the device.  mark us as needing
  868.     ;------ task attention.  Clear the quick flag
  869. BeginIO_QueueMsg:
  870.     bset    #UNITB_INTASK,UNIT_FLAGS(a3)
  871.     bclr    #IOB_QUICK,IO_FLAGS(a1)   ;We did NOT complete this quickly
  872.     ENABLE  a0
  873.  
  874.  
  875.     IFGE INFO_LEVEL-250
  876.      move.l  a1,-(sp)
  877.      move.l  a3,-(sp)
  878.      PUTMSG  250,<'%s/PutMsg: Port=%lx Message=%lx'>
  879.      addq.l  #8,sp
  880.     ENDC
  881.  
  882.     move.l  a3,a0
  883.     LINKSYS  PutMsg,md_SysLib(a6)   ;Port=a0, Message=a1
  884.     bra.s   BeginIO_End
  885.     ;----- return to caller before completing
  886.  
  887.  
  888.     ;------ Do it on the schedule of the calling process
  889.     ;------
  890. BeginIO_Immediate:
  891.     ENABLE  a0
  892.     bsr.s   PerformIO
  893.  
  894. BeginIO_End:
  895.     PUTMSG  200,<'%s/BeginIO_End'>
  896.     movem.l (sp)+,d1/a0/a3
  897.     rts
  898.  
  899. BeginIO_NoCmd:
  900.     move.b  #IOERR_NOCMD,IO_ERROR(a1)
  901.     bra.s   BeginIO_End
  902.  
  903.  
  904. ;
  905. ; PerformIO actually dispatches an io request.    It might be called from
  906. ; the task, or directly from BeginIO (thus on the callers's schedule)
  907. ;
  908. ; It expects a3 to already
  909. ; have the unit pointer in it.    a6 has the device pointer (as always).
  910. ; a1 has the io request.  Bounds checking has already been done on
  911. ; the I/O Request.
  912. ;
  913.  
  914. PerformIO:   ; ( iob:a1, unitptr:a3, devptr:a6 )
  915.     IFGE INFO_LEVEL-150
  916.      clr.l    -(sp)
  917.      move.w   IO_COMMAND(a1),2(sp)  ;Get entire word
  918.      PUTMSG   150,<'%s/PerformIO -- %ld'>
  919.      addq.l   #4,sp
  920.     ENDC
  921.  
  922.     moveq   #0,d0
  923.     move.b  d0,IO_ERROR(A1)     ; No error so far
  924.     move.b  IO_COMMAND+1(a1),d0 ;Look only at low byte
  925.     lsl.w   #2,d0        ; Multiply by 4 to get table offset
  926.     lea.l   cmdtable(pc),a0
  927.     move.l  0(a0,d0.w),a0
  928.  
  929.     jmp     (a0)    ;iob:a1  unit:a3  devprt:a6
  930.  
  931.  
  932.  
  933. ;
  934. ; TermIO sends the IO request back to the user.  It knows not to mark
  935. ; the device as inactive if this was an immediate request or if the
  936. ; request was started from the server task.
  937. ;
  938.  
  939. TermIO:      ; ( iob:a1, unitptr:a3, devptr:a6 )
  940.     PUTMSG  160,<'%s/TermIO'>
  941.     move.w  IO_COMMAND(a1),d0
  942.  
  943.     move.w  #IMMEDIATES,d1
  944.     btst    d0,d1
  945.     bne.s   TermIO_Immediate    ;IO was immediate, don't do task stuff...
  946.  
  947.     ;------ we may need to turn the active bit off.
  948.     btst    #UNITB_INTASK,UNIT_FLAGS(a3)
  949.     bne.s   TermIO_Immediate    ;IO was came from task, don't clear ACTIVE...
  950.  
  951.     ;------ the task does not have more work to do
  952.     bclr    #UNITB_ACTIVE,UNIT_FLAGS(a3)
  953.  
  954. TermIO_Immediate:
  955.     ;------ if the quick bit is still set then we don't need to reply
  956.     ;------ msg -- just return to the user.
  957.     btst    #IOB_QUICK,IO_FLAGS(a1)
  958.     bne.s   TermIO_End
  959.     LINKSYS ReplyMsg,md_SysLib(a6)      ;a1-message
  960.     ;(ReplyMsg sets the LN_TYPE to NT_REPLYMSG)
  961.  
  962. TermIO_End:
  963.     rts
  964.  
  965.  
  966. *****************************************************************************
  967. ;
  968. ; Here begins the functions that implement the device commands
  969. ; all functions are called with:
  970. ;   a1 -- a pointer to the io request block
  971. ;   a3 -- a pointer to the unit
  972. ;   a6 -- a pointer to the device
  973. ;
  974. ; Commands that conflict with 68000 instructions have a "My" prepended
  975. ; to them.
  976. ;----------------------------------------------------------------------
  977.  
  978. ;We can't AbortIO anything, so don't touch the IORequest!
  979. ;
  980. ;AbortIO() is a REQUEST to "hurry up" processing of an IORequest.
  981. ;If the IORequest was already complete, nothing happens (if an IORequest
  982. ;is quick or LN_TYPE=NT_REPLYMSG, the IORequest is complete).
  983. ;The message must be replied with ReplyMsg(), as normal.
  984. ;
  985. AbortIO:    ; ( iob: a1, device:a6 )
  986.     moveq   #IOERR_NOCMD,d0 ;return "AbortIO() request failed"
  987.     rts
  988.  
  989. RawRead:    ; 10 Not supported   (INVALID)
  990. RawWrite:    ; 11 Not supported   (INVALID)
  991. Invalid:
  992.     move.b  #IOERR_NOCMD,IO_ERROR(a1)
  993.     bra.s   TermIO
  994.  
  995. ;
  996. ; Update and Clear are internal buffering commands.  Update forces all
  997. ; io out to its final resting spot, and does not return until this is
  998. ; totally done.  Since this is automatic in a ramdisk, we simply return "Ok".
  999. ;
  1000. ; Clear invalidates all internal buffers.  Since this device
  1001. ; has no internal buffers, these commands do not apply.
  1002. ;
  1003. Update:
  1004. Clear:
  1005. MyReset:            ;Do nothing (nothing reasonable to do)
  1006. AddChangeInt:            ;Do nothing
  1007. RemChangeInt:            ;Do nothing
  1008. MyRemove:            ;Do nothing
  1009. Seek:                ;Do nothing
  1010. Motor:                ;Do nothing
  1011. ChangeNum:            ;Return zero (changecount =0)
  1012. ChangeState:            ;Zero indicates disk inserted
  1013. ProtStatus:            ;Zero indicates unprotected
  1014.     clr.l   IO_ACTUAL(a1)
  1015.     bra.s   TermIO
  1016.  
  1017.  
  1018. GetDriveType:            ;make it look like 3.5" (90mm) drive
  1019.     moveq   #DRIVE3_5,d0
  1020.     move.l  d0,IO_ACTUAL(a1)
  1021.     bra.s   TermIO
  1022.  
  1023.  
  1024. GetNumTracks:
  1025.     move.l  #RAMSIZE/BYTESPERTRACK,IO_ACTUAL(a1) ;Number of tracks
  1026.     bra.s   TermIO
  1027.  
  1028. ;
  1029. ; Foo and Bar are two device specific commands that are provided just
  1030. ; to show you how commands are added.  They currently return that
  1031. ; no work was done.
  1032. ;
  1033. Foo:
  1034. Bar:
  1035.     clr.l   IO_ACTUAL(a1)
  1036.     bra.s   TermIO
  1037.  
  1038.  
  1039. ;---------------------------------------------------------------------------
  1040. ; This device is designed so that no combination of bad
  1041. ; inputs can ever cause the device driver to crash.
  1042. ;---------------------------------------------------------------------------
  1043. RdWrt:
  1044.     IFGE INFO_LEVEL-200
  1045.     move.l    IO_LENGTH(a1),-(sp)
  1046.     PUTMSG    200,<'%s/RdWrt len %ld'>
  1047.     addq.l    #4,sp
  1048.     ENDC
  1049.  
  1050.     movem.l a2/a3,-(sp)
  1051.     move.l  a1,a2        ;Copy iob
  1052.     move.l  IO_UNIT(a2),a3      ;Get unit pointer
  1053.  
  1054. *      check operation for legality
  1055.     btst.b  #0,IO_DATA(a2)      ;check if user's pointer is ODD
  1056.     bne.s   IO_LenErr        ;bad...
  1057.     ;[D0=offset]
  1058.  
  1059.     move.l  IO_OFFSET(a2),d0
  1060.     move.l  d0,d1
  1061.     and.l   #SECTOR-1,d1    ;Bad sector boundary or alignment?
  1062.     bne.s   IO_LenErr        ;bad...
  1063.     ;[D0=offset]
  1064.  
  1065. *      check for IO within disc range
  1066.     ;[D0=offset]
  1067.     add.l   IO_LENGTH(a2),d0    ;Add length to offset
  1068.     bcs.s   IO_LenErr        ;overflow... (important test)
  1069.     cmp.l   #RAMSIZE,d0     ;Last byte is highest acceptable total
  1070.     bhi.s   IO_LenErr        ;bad... (unsigned compare)
  1071.     and.l   #SECTOR-1,d0    ;Even sector boundary?
  1072.     bne.s   IO_LenErr        ;bad...
  1073.  
  1074. *      We've gotten this far, it must be a valid request.
  1075.  
  1076.     IFD   INTRRUPT
  1077.      move.l   mdu_SigMask(a3),d0  ; Get signals to wait for
  1078.      LINKSYS  Wait,md_SysLib(a6)  ; Wait for interrupt before proceeding
  1079.     ENDC
  1080.  
  1081.  
  1082.     lea.l   mdu_RAM(a3),a0      ; Point to RAMDISK "sector" for I/O
  1083.     add.l   IO_OFFSET(a2),a0    ; Add offset to ram base
  1084.     move.l  IO_LENGTH(a2),d0
  1085.     move.l  d0,IO_ACTUAL(a2)    ; Indicate we've moved all bytes
  1086.     beq.s   RdWrt_end        ;---deal with zero length I/O
  1087.     move.l  IO_DATA(a2),a1      ; Point to data buffer
  1088. ;
  1089. ;A0=ramdisk index
  1090. ;A1=user buffer
  1091. ;D0=length
  1092. ;
  1093.     cmp.b   #CMD_READ,IO_COMMAND+1(a2)  ; Decide on direction
  1094.     BEQ.S   CopyTheBlock
  1095.     EXG     A0,A1        ; For Write and Format, swap source & dest
  1096. CopyTheBlock:
  1097.     LINKSYS CopyMemQuick,md_SysLib(a6)  ;A0=source A1=dest D0=size
  1098.     ;CopyMemQuick is very fast
  1099.  
  1100.  
  1101. RdWrt_end:
  1102.     move.l  a2,a1
  1103.     movem.l (sp)+,a2/a3
  1104.     bra     TermIO    ;END
  1105. IO_LenErr:
  1106.     move.b  #IOERR_BADLENGTH,IO_ERROR(a2)
  1107. IO_End:
  1108.     clr.l   IO_ACTUAL(a2)       ;Initially, no data moved
  1109.     bra.s   RdWrt_end
  1110.  
  1111.  
  1112. ;
  1113. ; the Stop command stop all future io requests from being
  1114. ; processed until a Start command is received.    The Stop
  1115. ; command is NOT stackable: e.g. no matter how many stops
  1116. ; have been issued, it only takes one Start to restart
  1117. ; processing.
  1118. ;
  1119. ;Stop is rather silly for a ramdisk
  1120. MyStop:
  1121.    PUTMSG   30,<'%s/MyStop: called'>
  1122.    bset   #MDUB_STOPPED,UNIT_FLAGS(a3)
  1123.    bra     TermIO
  1124.  
  1125.  
  1126. Start:
  1127.     PUTMSG   30,<'%s/Start: called'>
  1128.     bsr.s  InternalStart
  1129.     bra   TermIO
  1130.  
  1131.        ;[A3=unit A6=device]
  1132. InternalStart:
  1133.     move.l  a1,-(sp)
  1134.     ;------ turn processing back on
  1135.     bclr   #MDUB_STOPPED,UNIT_FLAGS(a3)
  1136.     ;------ kick the task to start it moving
  1137.     move.b  MP_SIGBIT(a3),d1
  1138.     CLEAR   d0
  1139.     bset    d1,d0            ;prepared signal mask
  1140.     move.l  MP_SIGTASK(a3),a1       ;:FIXED:marco-task to signal
  1141.     LINKSYS Signal,md_SysLib(a6)    ;:FIXED:marco-a6 not a3
  1142.     move.l  (sp)+,a1
  1143.     rts
  1144.  
  1145.  
  1146. ;
  1147. ; Flush pulls all I/O requests off the queue and sends them back.
  1148. ; We must be careful not to destroy work in progress, and also
  1149. ; that we do not let some io requests slip by.
  1150. ;
  1151. ; Some funny magic goes on with the STOPPED bit in here.  Stop is
  1152. ; defined as not being reentrant.  We therefore save the old state
  1153. ; of the bit and then restore it later.  This keeps us from
  1154. ; needing to DISABLE in flush.    It also fails miserably if someone
  1155. ; does a start in the middle of a flush. (A semaphore might help...)
  1156. ;
  1157.  
  1158. Flush:
  1159.    PUTMSG   30,<'%s/Flush: called'>
  1160.    movem.l   d2/a1/a6,-(sp)
  1161.  
  1162.    move.l   md_SysLib(a6),a6
  1163.  
  1164.    bset   #MDUB_STOPPED,UNIT_FLAGS(a3)
  1165.    sne     d2
  1166.  
  1167. Flush_Loop:
  1168.    move.l   a3,a0
  1169.    CALLSYS   GetMsg    ;Steal messages from task's port
  1170.  
  1171.    tst.l   d0
  1172.    beq.s   Flush_End
  1173.  
  1174.    move.l   d0,a1
  1175.    move.b   #IOERR_ABORTED,IO_ERROR(a1)
  1176.    CALLSYS   ReplyMsg
  1177.  
  1178.    bra.s   Flush_Loop
  1179.  
  1180. Flush_End:
  1181.    move.l   d2,d0
  1182.    movem.l   (sp)+,d2/a1/a6
  1183.  
  1184.    tst.b   d0
  1185.    beq.s   1$
  1186.  
  1187.    bsr     InternalStart
  1188. 1$:
  1189.    bra       TermIO
  1190.  
  1191.  
  1192. *****************************************************************************
  1193. ;
  1194. ; Here begins the task related routines
  1195. ;
  1196. ; A Task is provided so that queued requests may be processed at
  1197. ; a later time.  This is not very justifiable for a ram disk, but
  1198. ; is very useful for "real" hardware devices.  Take care with
  1199. ; your arbitration of shared hardware with all the multitasking
  1200. ; programs that might call you at once.
  1201. ;
  1202. ; Register Usage
  1203. ; ==============
  1204. ; a3 -- unit pointer
  1205. ; a6 -- syslib pointer
  1206. ; a5 -- device pointer
  1207. ; a4 -- task (NOT process) pointer
  1208. ; d7 -- wait mask
  1209. ;----------------------------------------------------------------------
  1210.  
  1211. ; some dos magic, useful for Processes (not us).  A process is started at
  1212. ; the first  executable address  after a segment list.    We hand craft a
  1213. ; segment list here.  See the the DOS technical reference if you really
  1214. ; need to know more about this.
  1215. ; The next instruction after the segment list is the first executable address
  1216.  
  1217.     cnop    0,4     ; long word align
  1218.     DC.L    16        ; segment length -- any number will do (this is 4
  1219.             ; bytes back from the segment pointer)
  1220. myproc_seglist:
  1221.     DC.L    0        ; pointer to next segment
  1222.  
  1223. Task_Begin:
  1224.     PUTMSG  35,<'%s/Task_Begin'>
  1225.     move.l  ABSEXECBASE,a6
  1226.  
  1227.     ;------ Grab the argument passed down from our parent
  1228.     move.l  4(sp),a3           ; Unit pointer
  1229.     move.l  mdu_Device(a3),a5  ; Point to device structure
  1230.  
  1231.     IFD   INTRRUPT
  1232.      ;------ Allocate a signal for "I/O Complete" interrupts
  1233.      moveq   #-1,d0        ; -1 is any signal at all
  1234.      CALLSYS   AllocSignal
  1235.      move.b   d0,mdu_SigBit(A3)   ; Save in unit structure
  1236.      moveq   #0,d7       ; Convert bit number signal mask
  1237.      bset   d0,d7
  1238.      move.l   d7,mdu_SigMask(A3)   ; Save in unit structure
  1239.      lea.l   mdu_is(a3),a1      ; Point to interrupt structure
  1240.      moveq   #INTB_PORTS,d0    ; Portia interrupt bit 3
  1241.      CALLSYS AddIntServer    ; Now install the server
  1242.      move.l   md_Base(a5),a0      ; Get board base address
  1243. *    bset.b   #INTENABLE,INTCTRL2(a0)   ; Enable interrupts
  1244.     ENDC
  1245.  
  1246.     ;------ Allocate a signal
  1247.     moveq   #-1,d0        ; -1 is any signal at all
  1248.     CALLSYS AllocSignal
  1249.     move.b  d0,MP_SIGBIT(a3)
  1250.     move.b  #PA_SIGNAL,MP_FLAGS(a3) ;Make message port "live"
  1251.     ;------ change the bit number into a mask, and save in d7
  1252.     moveq   #0,d7    ;Clear D7
  1253.     bset    d0,d7
  1254.  
  1255.     IFGE INFO_LEVEL-40
  1256.      move.l  $114(a6),-(sp)
  1257.      move.l  a5,-(sp)
  1258.      move.l  a3,-(sp)
  1259.      move.l  d0,-(sp)
  1260.      PUTMSG  40,<'%s/Signal=%ld, Unit=%lx Device=%lx Task=%lx'>
  1261.      add.l   #4*4,sp
  1262.     ENDC
  1263.  
  1264.     bra.s   Task_StartHere
  1265.  
  1266. ; OK, kids, we are done with initialization.  We now can start the main loop
  1267. ; of the driver.  It goes like this.  Because we had the port marked
  1268. ; PA_IGNORE for a while (in InitUnit) we jump to the getmsg code on entry.
  1269. ; (The first message will probably be posted BEFORE our task gets a chance
  1270. ; to run)
  1271. ;------     wait for a message
  1272. ;------     lock the device
  1273. ;------     get a message.  If no message, unlock device and loop
  1274. ;------     dispatch the message
  1275. ;------     loop back to get a message
  1276.  
  1277.     ;------ no more messages.  back ourselves out.
  1278. Task_Unlock:
  1279.     and.b   #$ff&(~(UNITF_ACTIVE!UNITF_INTASK)),UNIT_FLAGS(a3)
  1280.     ;------ main loop: wait for a new message
  1281.  
  1282. Task_MainLoop:
  1283.     PUTMSG   75,<'%s/++Sleep'>
  1284.     move.l  d7,d0
  1285.     CALLSYS Wait
  1286.     IFGE INFO_LEVEL-5
  1287.     bchg.b    #1,$bfe001  ;Blink the power LED
  1288.     ENDC
  1289. Task_StartHere:
  1290.     PUTMSG   75,<'%s/++Wakeup'>
  1291.     ;------ see if we are stopped
  1292.     btst    #MDUB_STOPPED,UNIT_FLAGS(a3)
  1293.     bne.s   Task_MainLoop    ; device is stopped, ignore messages
  1294.     ;------ lock the device
  1295.     bset    #UNITB_ACTIVE,UNIT_FLAGS(a3)
  1296.     bne     Task_MainLoop    ; device in use (immediate command?)
  1297.  
  1298.  
  1299.    ;------ get the next request
  1300. Task_NextMessage:
  1301.     move.l  a3,a0
  1302.     CALLSYS GetMsg
  1303.     PUTMSG  1,<'%s/GotMsg'>
  1304.     tst.l   d0
  1305.     beq     Task_Unlock ; no message?
  1306.  
  1307.     ;------ do this request
  1308.     move.l  d0,a1
  1309.     exg     a5,a6    ; put device ptr in right place
  1310.     bsr     PerformIO
  1311.     exg     a5,a6    ; get syslib back in a6
  1312.  
  1313.     bra.s   Task_NextMessage
  1314.  
  1315. *****************************************************************************
  1316. ;
  1317. ; Here is a dummy interrupt handler, with some crucial components commented
  1318. ; out.    If the IFD INTRRUPT is enabled, this code will cause the device to
  1319. ; wait for a level two interrupt before it will process each request
  1320. ; (pressing RETURN on the keyboard will do it).  This code is normally
  1321. ; disabled, and must fake or omit certain operations since there  isn't
  1322. ; really any hardware for this driver.    Similar code has been used
  1323. ; successfully in other, "REAL" device drivers.
  1324. ;
  1325.  
  1326.    IFD     INTRRUPT
  1327.  
  1328. ;   A1 should be pointing to the unit structure upon entry! (IS_DATA)
  1329. myintr:
  1330. *      move.l    md_Base(a0),a0      ; point to board base address
  1331. *      btst.b    #IAMPULLING,INTCTRL1(a0);See if I'm interrupting
  1332. *      beq.s   myexnm          ; if not set, exit, not mine
  1333. *      move.b    #0,INTACK(a0)      ; toggle controller's int2 bit
  1334.  
  1335. ;      ------ signal the task that an interrupt has occurred
  1336.  
  1337.     move.l    mdu_Device(a1),a0   ; Get device pointer
  1338.     move.l    mdu_SigMask(a1),d0
  1339.     lea.l    mdu_tcb(a1),a1
  1340.     move.l    md_SysLib(a0),a6   ; Get pointer to system
  1341.     CALLSYS Signal
  1342.  
  1343. ;      now clear the zero condition code so that
  1344. ;      the interrupt handler doesn't call the next
  1345. ;      interrupt server.
  1346. ;
  1347. *      moveq   #1,d0         clear zero flag
  1348. *      bra.s   myexit          now exit
  1349. ;
  1350. ;      this exit point sets the zero condition code
  1351. ;      so the interrupt handler will try the next server
  1352. ;      in the interrupt chain
  1353. ;
  1354. myexnm        moveq   #0,d0      set zero condition code
  1355. ;
  1356. myexit        rts
  1357.    ENDC
  1358.  
  1359.  
  1360. *****************************************************************************
  1361.  
  1362. mdu_Init:
  1363. ;   ------ Initialize the device
  1364.  
  1365.     INITBYTE    MP_FLAGS,PA_IGNORE  ;Unit starts with a message port
  1366.     INITBYTE    LN_TYPE,NT_MSGPORT  ;
  1367.     INITLONG    LN_NAME,myName        ;
  1368.     INITLONG    mdu_tcb+LN_NAME,myName
  1369.     INITBYTE    mdu_tcb+LN_TYPE,NT_TASK
  1370.     INITBYTE    mdu_tcb+LN_PRI,5
  1371.     IFD   INTRRUPT
  1372.      INITBYTE     mdu_is+LN_PRI,4      ; Int priority 4
  1373.      INITLONG     mdu_is+IS_CODE,myintr    ; Interrupt routine addr
  1374.      INITLONG     mdu_is+LN_NAME,myName
  1375.     ENDC
  1376.     DC.L   0
  1377.  
  1378.  IFNE  AUTOMOUNT
  1379. mdn_Init:
  1380. *   ;------ Initialize packet for MakeDosNode
  1381.  
  1382.     INITLONG    mdn_execName,myName    ; Address of driver name
  1383.     INITLONG    mdn_tableSize,11    ; # long words in AmigaDOS env.
  1384.     INITLONG    mdn_dName,$524d0000    ; Store 'RM' in name
  1385.     INITLONG    mdn_sizeBlock,SECTOR/4    ; # longwords in a block
  1386.     INITLONG    mdn_numHeads,1        ; RAM disk has only one "head"
  1387.     INITLONG    mdn_secsPerBlk,1    ; secs/logical block, must = "1"
  1388.     INITLONG    mdn_blkTrack,SECTORSPER ; secs/track (must be reasonable)
  1389.     INITLONG    mdn_resBlks,1        ; reserved blocks, MUST > 0!
  1390.     INITLONG    mdn_upperCyl,(RAMSIZE/BYTESPERTRACK)-1 ; upper cylinder
  1391.     INITLONG    mdn_numBuffers,1    ; # AmigaDOS buffers to start
  1392.     DC.L   0
  1393.  ENDC
  1394.  
  1395. ;----------------------------------------------------------------------
  1396. ; EndCode is a marker that shows the end of your code.    Make sure it does not
  1397. ; span hunks, and is not before the rom tag!  It is ok to put it right after
  1398. ; the rom tag -- that way you are always safe.    I put it here because it
  1399. ; happens to be the "right" thing to do, and I know that it is safe in this
  1400. ; case (this program has only a single code hunk).
  1401. ;----------------------------------------------------------------------
  1402. EndCode:    END
  1403.